#pragma once
#include <d2d1.h>
#include <wincodec.h>
#include <mutex>
#include <ctime>
#include <future>

#include "Resource.h"
#include "Tool.h"
#include "Message.h"

namespace core{

    namespace display{
        enum PlayStatus : int{
            Idle,
            Ready,
            Playing,
            Stopped,
            _,
        }; 

        namespace res = util::resource;
        namespace tool = util::tool;
        namespace msg = util::message;
        class ResourceFactory{
        public:
            ResourceFactory();

            HRESULT Initialize();

            bool HasInitialized();

            bool IsBuilding();
            //id defined in ResDef.h
            HRESULT GetWICBitmapFromId(int id);

            HRESULT GetWICBitmapFromMemory(res::BinFile *pfile);

            HRESULT CreateD2DBitmap(ID2D1Bitmap ** ppd2d_bmp, ID2D1RenderTarget* pTarget);

            ~ResourceFactory();
        private:
            IWICImagingFactory *pIWICFactory; 
            IWICBitmapSource * pSource;
        };

        extern ResourceFactory INSTANCE;

        enum AnimationProcessMode : short{
            CONSTRAINED = 1
        };

        class AbstractPlayer{
        public:
            AbstractPlayer();

            virtual void Reset() = 0;

            virtual HRESULT Play() = 0;

            virtual HRESULT Paint() = 0;

            virtual D2D1_RECT_F GetRect() = 0;

            virtual PlayStatus GetStatus() = 0;

            virtual bool SetEventListener(msg::IUniqueMessageHandler * plistener);

            //Deprecated
            // virtual std::future<bool> AsycStartPlay(bool * is_playing){return std::async(std::launch::async,[]()->bool{return true;});};

            virtual bool GetCurrentFrameD2DBitmap(ID2D1Bitmap ** ppbmp) = 0;

            template<typename T>
            T SafeCall(T (*consumer)(AbstractPlayer *player));

            template<typename T>
            T SafeCall(T (AbstractPlayer::*consumer)());
            //上面两个模板实现了void特化

            template<typename T, typename First, typename ...Arg>
            T SafeCall(typename tool::MultFunction<T, AbstractPlayer *, First, Arg...>::value func, First arg1, Arg... arg);
        protected:
            std::unique_lock<std::mutex> * current_lock_ptr;
            msg::IUniqueMessageHandler * pListener;
        private:
            std::mutex player_lock;
        };


        class AnimationProcess : public AbstractPlayer{
        public:
            AnimationProcess(const D2D1_RECT_F& rect);

            AnimationProcess();

            void SetRect(const D2D1_RECT_F& rect);

            // void SetLock(const std::mutex& lock);

            void SetRender(ID2D1RenderTarget * prender);

            void SetInterval(float itrl);

            HRESULT Prepare(int * id_arr, int count);

            HRESULT Prepare(int from_id, int to_id);

            HRESULT Prepare(ID2D1Bitmap **parr, int count);

            bool PlayNextFrame();

            bool IsReadyForNextFrame();

            virtual void Reset();

            virtual HRESULT Play();
        
            virtual HRESULT Paint();

            virtual PlayStatus GetStatus();

            virtual D2D1_RECT_F GetRect();

            // virtual std::future<bool> AsycStartPlay(bool * is_playing);

            virtual bool GetCurrentFrameD2DBitmap(ID2D1Bitmap ** ppbmp);

            virtual ~AnimationProcess();
        protected:
            void ClearWhatItPaint();
        private:
            int frame_count;
            D2D1_RECT_F rect;
            ID2D1RenderTarget * prender;
            ID2D1Bitmap ** bmp_quene;
            int playing_bmp_iptr;
            PlayStatus stats;
            short mode;
            float frame_interval;
            time_t last_t;
        };

        class AnimationManager : public msg::IUniqueMessageHandler{
        public:
            AnimationManager();
            ~AnimationManager();
            bool IsPlaying();
            void Play(AbstractPlayer * player);
            void OnPaint();
            virtual bool Receive(msg::MessageType msg, void * param);
        private:
            bool is_playing_handle;
            bool is_preparing;
            AbstractPlayer * pplayer;
        };
        
       
        bool EnableAlphaCompositing(HWND hWnd);
    }
}